package bj.gui

import scala.swing.event
import event.ButtonClicked
import scala.swing.SimpleGUIApplication
import scala.swing.Frame
import scala.swing.Label
import scala.swing.Swing
import scala.swing.Button
import scala.swing.FlowPanel
import scala.swing.GridPanel
import scala.swing.BorderPanel
import scala.swing.BoxPanel
import scala.swing.Orientation
import scala.swing.CheckBox
import javax.swing.SpinnerNumberModel
import javax.swing.JSpinner
import scala.swing.Component
import scala.swing.TextArea
import scala.swing.ScrollPane
import java.awt.Dimension
import java.awt.Color
import java.awt.GridLayout
import scala.swing.Panel
import javax.swing.JPanel
import java.awt.Graphics
import java.awt.Graphics2D
import javax.swing.JComponent
import scala.swing.MainFrame
import javax.swing.ImageIcon
import java.awt.Image
import javax.swing.event.ChangeListener
import scala.swing.event.ActionEvent
import javax.swing.event.ChangeEvent
import bj.actor.Dealer
import bj.actor.HumanPlayer
import bj.actor.Send
import bj.actor.Receive
import bj.actor.Intermediator
import bj.actor.SendMsg
import bj.actor.Observe
import bj.actor.ObserveH
import bj.actor.Hole
import bj.actor.Up
import bj.actor.DealerCard
import bj.actor.ObserveSplit
import bj.actor.DealtCard
import bj.actor.PlayHand
import bj.actor.Go
import bj.actor.PlacedBet
import bj.actor.PlaceBet
import bj.actor.TableStatus
import bj.actor.PlayerStatus
import bj.actor.GetTableStatus
import bj.actor.TableInfo
import bj.actor.LeaveTable
import bj.actor.Hit
import bj.actor.Stay
import bj.actor.DoubleDown
import bj.actor.Split
import bj.actor.Surrender
import bj.actor.Insurance
import bj.actor.OfferInsurance
import bj.actor.InsuranceClosed
import bj.actor.Win
import bj.actor.Lose
import bj.actor.Push
import bj.actor.Registered
import bj.actor.RegisterGUI
import bj.hkeeping.Broke
import bj.util.Logs
import bj.card.Card
import bj.card.Hand
import org.apache.log4j.PropertyConfigurator
import javax.swing.border.Border
import javax.swing.BorderFactory
import scala.actors.OutputChannel
import scala.actors.Actor
import scala.actors.remote.RemoteActor
import scala.actors.remote.Node
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.io.File
import java.awt.GraphicsConfiguration
import java.awt.Transparency
import javax.swing.Icon
import bj.Config

class PlayerTable(remoteActor: Symbol) extends Frame with Logs {
  javax.swing.UIManager.setLookAndFeel(Constants.LF)
  PropertyConfigurator.configure("log4j.properties")
  import javax.swing.WindowConstants.DISPOSE_ON_CLOSE
  peer.setDefaultCloseOperation(DISPOSE_ON_CLOSE)

  override def closeOperation() { standUpFromTable() }

  val actionLabel = new Label {
    text = ""
    border = Swing.EmptyBorder(10)
  }
  var myBankroll = 0.0
  var maxBet = 0.0
  var minBet = 0.0
  def top = mainFrame

  // Set up p which is a panel containing the table silhouette and the player names/cards. All the action happens here.  
  val p = new GridPanel(4, 1) {
    override def paintComponent(g: Graphics2D) {
      g.setColor(Constants.greenBack)
      g.fillRect(0, 0, size.width, size.height)
      g.setColor(Constants.tableColor)
      g.fillOval(65, 45, 550, 300)
      g.setColor(Constants.greenBack)
      g.fillOval(175, 0, 325, 150)
    }

    // nameLabels is an array of 6 labels signifying who is sitting at each seat at the table (or an underscore if the seat is empty)
    var nameLabels: Array[Label] = new Array(6)
    (0 to 5).foreach(x => nameLabels(x) = new Label { text = "__________" })

    // Variables to show dealer's name and dealer's cards
    val dealerUp = new Label { icon = cardIcon("blank") }
    val dealerDown = new Label { icon = cardIcon("blank") }
    val dealerName = new Label { text = "" }
    val dealerCards = new FlowPanel {
      contents += dealerUp
      contents += dealerDown
      opaque = false
    }

    // Array of 6 arrays of 2 FlowPanels. Each player has a panel to show each hand (up to 2, if the player splits)
    var panelForPlayer: Array[Array[FlowPanel]] = new Array(6);
    (0 to 5).foreach(x => createPanels(x));
    def createPanels(seatNum: Int): Unit = {
      panelForPlayer(seatNum) = new Array(2);
      panelForPlayer(seatNum)(0) = new FlowPanel() { opaque = false }
      panelForPlayer(seatNum)(1) = new FlowPanel() { opaque = false }
    }

    // Array of 6 labels (1 per player) that will indicate if the player has split
    var playerDivider: Array[Label] = new Array(6);
    (0 to 5).foreach(x => playerDivider(x) = new Label() { text = "" })

    // Array of 6 BorderPanels.  Each player has a panel to store his hand(s). If there are 2 hands (player has split) then the playerDivider will be an icon that says "SPLIT"
    var panelForHands: Array[BorderPanel] = new Array(6);
    (0 to 5).foreach(x => panelForHands(x) = new BorderPanel() {
      add(panelForPlayer(x)(0), BorderPanel.Position.West)
      add(panelForPlayer(x)(1), BorderPanel.Position.East)
      add(playerDivider(x), BorderPanel.Position.Center)
      opaque = false
    })

    // Array of 6 FlowPanels, one per player. Each one has the player's hand(s) prepended or appended (depending on what side of the table the player is sitting at) with the player's name
    var panels: Array[FlowPanel] = new Array(6);
    (0 to 5).foreach(x => panels(x) = new FlowPanel() {
      if (x < 3)
        contents += nameLabels(x)
      contents += panelForHands(x)
      if (x >= 3)
        contents += nameLabels(x)
      opaque = false
    })

    val dealerNamePanel = new FlowPanel() {
      contents += dealerName
      opaque = false
    }
    val dealerPanel = new BoxPanel(Orientation.Vertical) {
      contents += dealerNamePanel
      contents += dealerCards
      opaque = false
    }

    val row1Panel = new BorderPanel() {
      add(panels(0), BorderPanel.Position.West)
      add(panels(5), BorderPanel.Position.East)
      // Indent both sides 25 px so that the player names show up close to the edges of the table silhouette
      border = Swing.EmptyBorder(0, 25, 0, 25)
      opaque = false
    }
    val row2Panel = new BorderPanel() {
      add(panels(1), BorderPanel.Position.West)
      add(panels(4), BorderPanel.Position.East)
      // No need to indent; the player names will show up at the very edges of the table silhouette at its widest point
      border = Swing.EmptyBorder(0, 0, 0, 0)
      opaque = false
    }
    val row3Panel = new BorderPanel() {
      add(panels(2), BorderPanel.Position.West)
      add(panels(3), BorderPanel.Position.East)
      // Indent both sides 100 px so that the player names show up close to the edges of the table silhouette
      border = Swing.EmptyBorder(0, 100, 0, 100)
      opaque = false
    }

    //Add everything to p panel: each takes an equal amount of vertical space
    contents += dealerPanel
    contents += row1Panel
    contents += row2Panel
    contents += row3Panel

  }

  val mainFrame = new MainFrame {
    title = "Blackjack - Table View"
    minimumSize = Constants.playerTableSize
    maximumSize = Constants.playerTableSize
    //Don't allow resize
    resizable = false

    background = Constants.greenBack

    val patterArea = new TextArea {
    }

    val bankrollLabel = new Label { text = "Bankroll: " }
    val minBetLabel = new Label { text = "Min bet: " }
    val maxBetLabel = new Label { text = "Max bet: " }
    val sitOutCheckBox = new CheckBox {
      text = "Sit Out"
      background = Constants.greenBack
    }

    val moneyModel = new SpinnerNumberModel(0.00, 0.00, 5000.00, 1)
    val moneySpinner = new JSpinner(moneyModel) with ChangeListener {
      addChangeListener(this)
      def stateChanged(e: ChangeEvent) = {
        var doubleVal = this.getValue().toString().toDouble
        if (doubleVal < minBet)
          doubleVal = minBet
        else if (doubleVal > maxBet)
          doubleVal = maxBet
        println("double: " + doubleVal)
        removeChangeListener(this) /* Prevent recursive calls to stateChanged */
        this.setValue(doubleVal)
        addChangeListener(this) /* Listen to events again */
      }
    }

    moneySpinner.setEditor(new JSpinner.NumberEditor(moneySpinner, "0.00"))

    val invisibleButton = new Button
    val hitButton = new Button { text = "Hit" }
    val standButton = new Button { text = "Stand" }
    val doubleDownButton = new Button { text = "Double Down" }
    val splitButton = new Button { text = "Split" }
    val insuranceButton = new Button { text = "Insurance" }
    val surrenderButton = new Button { text = "Surrender" }
    val leaveButton = new Button { text = "Leave Table" }
    val patterButton = new Button { text = "Turn off patter" }
    val betButton = new Button {
      text = "Bet"
      enabled = true
    }

    val actbuttonPanel = new FlowPanel() {
      contents += hitButton
      contents += standButton
      contents += doubleDownButton
      contents += splitButton
      contents += insuranceButton
      contents += surrenderButton
      background = Constants.greenBack
      contents.foreach(x => x.enabled = false)
    }

    val buttonPanel = new BoxPanel(Orientation.Vertical) {
      contents += leaveButton
      contents += patterButton
      background = Constants.greenBack
    }

    val topPanel = new FlowPanel() {
      contents += actionLabel
      background = Constants.greenBack
    }
    val midPanel = new BorderPanel() {
      add(p, BorderPanel.Position.Center)
      add(actbuttonPanel, BorderPanel.Position.South)
      background = Constants.greenBack

    }
    val scroll = new ScrollPane {
      viewportView = patterArea
      preferredSize = new Dimension(500, 60)
    }

    val bottomPanel = new FlowPanel() {
      contents += scroll
      contents += patterButton
      contents += leaveButton
      contents += new Label {
        icon = new ImageIcon("images/BJLogo2.gif")
      }
      background = Constants.greenBack
    }

    val eastPanel = new BoxPanel(Orientation.Vertical) {
      contents += bankrollLabel
      contents += minBetLabel
      contents += maxBetLabel
      contents += new FlowPanel {
        contents += Component.wrap(moneySpinner)
        background = Constants.greenBack
      }
      contents += new FlowPanel {
        contents += betButton
        background = Constants.greenBack
      }
      contents += sitOutCheckBox
      background = Constants.greenBack
    }

    contents = new BorderPanel() {

      add(topPanel, BorderPanel.Position.North)
      add(midPanel, BorderPanel.Position.Center)
      add(bottomPanel, BorderPanel.Position.South)
      add(new FlowPanel() {
        contents += eastPanel
        background = Constants.greenBack
      }, BorderPanel.Position.East)
      background = Constants.greenBack
    }

    // Listen to the HumanPlayer object representing me so I can process events received from it
    implicit val intermediator = Intermediator(RemoteActor.select(Node(Config.IPAddress, Config.PortNumber), remoteActor))

    listenTo(intermediator)
    listenTo(invisibleButton)
    listenTo(hitButton)
    listenTo(standButton)
    listenTo(doubleDownButton)
    listenTo(splitButton)
    listenTo(surrenderButton)
    listenTo(insuranceButton)
    listenTo(leaveButton)
    listenTo(patterButton)
    listenTo(betButton)
    listenTo(sitOutCheckBox)
    reactions += {
      case ButtonClicked(`invisibleButton`) =>
        debug("refresh")
      // Update the GUI for when the player Hits and send a message to HumanPlayer to relay on to the Dealer. 
      // HumanPlayer will fill in the right pid and handIndex; -1 is used here to more easily catch errors (since
      // 0 is valid for both pid and handIndex)
      case ButtonClicked(`hitButton`) =>
        println("Hit button clicked")
        Send(Hit(-1, -1))
        playerHit()
      // Update the GUI for when the player Stands and send a message to HumanPlayer to relay on to the Dealer. 
      case ButtonClicked(`standButton`) =>
        println("Stand button clicked")
        Send(Stay(-1, -1))
        playerStood()
      // Update the GUI for when the player Doubles and send a message to HumanPlayer to relay on to the Dealer.  
      case ButtonClicked(`doubleDownButton`) =>
        println("Double Down button clicked")
        playerDoubled()
        Send(DoubleDown(-1, -1))
      // Update the GUI for when the player Splits and send a message to HumanPlayer to relay on to the Dealer. 
      // HumanPlayer will fill in the right pid; handIndex does not apply since the game only supports splitting once.
      case ButtonClicked(`splitButton`) =>
        println("Split button clicked")
        Send(Split(-1))
      // Send a message to HumanPlayer that the player bought insurance.  Insurance only applies right after Deal so handIndex does not apply.     
      case ButtonClicked(`insuranceButton`) =>
        println("Insurance button clicked")
        patterArea.text += "You bought some insurance. You obviously haven't read Blackjack for Dummies ...\n"
        Send(Insurance(-1))
      // Update the GUI for when the player Surrenders and send a message to HumanPlayer to relay on to the Dealer. 
      case ButtonClicked(`surrenderButton`) =>
        println("Surrender button clicked")
        playerSurrendered()
        Send(Surrender(-1, -1))
      // Leave the table either by clicking the button or closing the window (see the override for method closeOperation at the top of this class)
      // This is not implemented in the Game yet; the player is not actually deregistered from the table
      case ButtonClicked(`leaveButton`) =>
        println("Leave button clicked")
        Send(LeaveTable)
        this.close()
      // Turn the patter area on or off. The text gets updated even if it's off; this button just hides or shows the area  
      case ButtonClicked(`patterButton`) =>
        println("Patter button clicked")
        if (patterArea.visible) {
          println("Turning patter area off")
          patterArea.visible = false
          scroll.visible = false
          patterButton.text = "Turn on patter"
        } else {
          println("Turning patter area on")
          patterArea.visible = true
          scroll.visible = true
          patterButton.text = "Turn off patter"
        }
      // Send my bet to the game.  When my bet is sent, clear the cards from the previous hand in preparation for a new hand being dealt  
      case ButtonClicked(`betButton`) =>
        println("Bet button clicked")
        betButton.enabled = false
        Send(PlacedBet(moneySpinner.getValue().toString().toDouble))
        patterArea.text += "You placed your bet of $" + String.format("%3.2f", double2Double(moneySpinner.getValue().toString().toDouble)) + ".\n"
        actionLabel.text = ""
        betButton.enabled = false
        clearTable()
      // This function is not yet implemented.    
      case ButtonClicked(`sitOutCheckBox`) =>
        if (sitOutCheckBox.selected) {
          println("Player is sitting out")
          actionLabel.text = "Watch and learn"
        } else {
          println("Player is not sitting out any more")
          actionLabel.text = "Place your bet!"
        }

      // Receive events come from the HumanPlayer
      // Receive an Observe message that a player (possibly me) was dealt a card
      case Receive(Observe(card, player, hand)) =>
        showCard(card, player, false, hand)
      // Receive an ObserveH message that a player (possibly me) was dealt a card after doubling down. Show that card rotated 90 degrees
      case Receive(ObserveH(card, player, hand)) =>
        showCard(card, player, true, hand)
      // Receive a PlayHand message that indicates it is my turn to play
      case Receive(PlayHand(splitPossible)) =>
        promptToPlay(splitPossible)
      // Receive a Broke message that indicates that I broke
      case Receive(Broke) => playerBroke()
      // Receive a TableStatus message that is a response to GetTableStatus. The value is an array of player information used to update the display on the GUI
      case Receive(TableStatus(status)) => setTable(status)
      // Receive a PlayerStatus message that is a response to GetTableStatus. The value is the name of the current player and his or her bankroll
      case Receive(PlayerStatus(name, bankroll)) =>
        myBankroll = bankroll
        bankrollLabel.text = "Bankroll: $" + String.format("%3.2f", double2Double(myBankroll))
      // Receive an ObserveSplit message indicating that a player (might be me) has split his or her hand. 
      case Receive(ObserveSplit(player)) => showSplit(player)
      // Receive a PlaceBet message that indicates I need to place a bet
      case Receive(PlaceBet) => promptToBet()
      // Receive a TableLimits message that is a response to GetTableStatus. The values are the min and max bet allowed at the table
      case Receive(TableInfo(min, max, dealer)) =>
        minBetLabel.text = "Min bet: $" + String.format("%3.2f", double2Double(min))
        maxBet = max
        minBet = min
        moneySpinner.setValue(minBet)
        maxBetLabel.text = "Max bet: $" + String.format("%3.2f", double2Double(max))
        p.dealerName.text = dealer
      // Receive an OfferInsurance message that the dealer's up card is an A so insurance may be purchased
      case Receive(OfferInsurance) => promptForInsurance()
      // Receive an InsuranceClosed message that 3 seconds have passed and the dealer closed the opportunity to purchase insurance
      case Receive(InsuranceClosed) => removePromptForInsurance()
      // Receive a Hole message indicating what the dealer's down card was
      case Receive(Hole(card)) =>
        debug("Got hole card: " + card)
        p.dealerDown.icon = cardIcon(card.toImageName)
        patterArea.text += "Dealer's hole card is " + card.toString() + "\n"
      // Receive an Up message indicating what the dealer's up card is.  This is not our cue to play; just display the card  
      case Receive(Up(card)) =>
        p.dealerUp.icon = cardIcon(card.toImageName)
        patterArea.text += "Dealer's up card is " + card.toString() + "\n"
      // Receive a DealerCard message indicating that the dealer has hit his or her hand.  Append this card to the dealer's hand on the GUI  
      case Receive(DealerCard(card)) =>
        debug("Received dealer card " + card)
        p.dealerCards.contents.append(new Label { icon = cardIcon(card.toImageName) })
        p.dealerCards.revalidate()
        debug("Dealer played")
        patterArea.text += "Dealer hit and received a " + card.toString() + "\n"
      // Receive a Win message indicating that I won this hand.  Display this as a pop up message to the player and update the Bankroll  
      case Receive(Win(bank)) =>
        patterArea.text += "You won! Your bankroll is $" + String.format("%3.2f", double2Double(bank)) + "\n"
        new BjInfoBox(new Label { text = "You won! Your bankroll is $" + String.format("%3.2f", double2Double(bank)) })
        actionLabel.text = "You won!"
        myBankroll = bank
      // Receive a Lose message indicating that I lost this hand. Display as a pop up message and update the Bankroll    
      case Receive(Lose(bank)) =>
        patterArea.text += "You lost! Your bankroll is $" + String.format("%3.2f", double2Double(bank)) + "\n"
        new BjInfoBox(new Label { text = "You lost! Your bankroll is $" + String.format("%3.2f", double2Double(bank)) })
        actionLabel.text = "You lost!"
        myBankroll = bank
      // Receive a Push message indicating that I pushed on this hand. Display as a pop up message and update the Bankroll    
      case Receive(Push(bank)) =>
        patterArea.text += "You pushed! Your bankroll is $" + String.format("%3.2f", double2Double(bank)) + "\n"
        new BjInfoBox(new Label { text = "You pushed! Your bankroll is $" + String.format("%3.2f", double2Double(bank)) })
        actionLabel.text = "You pushed!"
        myBankroll = bank
      // Receive a message from the HumanPlayer.  As of now, the only time this happens is when there is a lag in disabling the Insurance button and
      // by the time insurance is purchased the window is closed. This is just appended to the patter and no further action is taken.  
      case Receive(SendMsg(msg)) =>
        patterArea.text += msg + "\n"
      // Receive a Registered message indicating that I have been registered to play at the table. At this point it's safe to send a GetTableStatus 
      // request because the HumanPlayer knows who I am and who the dealer of my table is  
      case Receive(Registered(seat)) =>
        Send(GetTableStatus)
    }
    // Send a message to the HumanPlayer to fill in the GUI mailbox pointer
    Send(RegisterGUI)
    visible = true
  }

  // Prompt the player to buy insurance if he or she wishes
  def promptForInsurance(): Unit = {
    top.insuranceButton.enabled = true
    actionLabel.text = "Option to purchase insurance"
    top.patterArea.text += "Dealer offered the option to buy insurance\n"
  }

  // Disallow the purchase of insurance after 3 second delay
  def removePromptForInsurance(): Unit = {
    top.insuranceButton.enabled = false
    actionLabel.text = "Insurance closed"
    top.patterArea.text += "Dealer closed the option to buy insurance\n"
  }

  // Update the min bet and max bet labels for this table
  def updateLimits(min: Double, max: Double): Unit = {
    top.minBetLabel.text = "Min bet: $" + String.format("%3.2f", double2Double(min))
    top.maxBetLabel.text = "Max bet: $" + String.format("%3.2f", double2Double(max))
  }

  // Show a card that was dealt to the indicated player position/hand. Indicate if the card is the result of a double down and should be shown horizontally
  def showCard(card: Card, player: Int, horizontal: Boolean, hand: Int): Unit = {
    if (player > 2) {
      if (horizontal)
        p.panelForPlayer(player)(hand).contents.prepend(new Label { icon = horizontalCardIcon(card.toImageName) })
      else
        p.panelForPlayer(player)(hand).contents.prepend(new Label { icon = cardIcon(card.toImageName) })
    } else {
      if (horizontal)
        p.panelForPlayer(player)(hand).contents.append(new Label { icon = horizontalCardIcon(card.toImageName) })
      else
        p.panelForPlayer(player)(hand).contents.append(new Label { icon = cardIcon(card.toImageName) })
    }
    p.panels(player).revalidate()
    top.patterArea.text += p.nameLabels(player).text + " was dealt a " + card.toString() + "\n"
  }

  // Show a card (represented by a string and not a Card object, so this is used to show a card back or a blank card) to the indicated player position
  def showCard(card: String, player: Int, horizontal: Boolean, hand: Int): Unit = {
    if (player > 2)
      p.panelForPlayer(player)(0).contents.prepend(new Label { icon = cardIcon(card) })
    else
      p.panelForPlayer(player)(0).contents.append(new Label { icon = cardIcon(card) })
    p.panels(player).revalidate()
  }

  // Return the given card input as a String (such as D7 for 7 of diamonds) as an ImageIcon sized to fit on the table
  def cardIcon(cardFile: String): ImageIcon = {
    val myIcon2 = new ImageIcon("images/cards/" + cardFile + ".png")
    val img = myIcon2.getImage();
    val newimg = img.getScaledInstance(40, 54, java.awt.Image.SCALE_SMOOTH)
    val myIcon1 = new ImageIcon(newimg)
    myIcon1
  }

  // Return the given card as an ImageIcon sized to fit on the table. The card will be rotated 90 degrees to indicate the card was hit after a double down
  def horizontalCardIcon(cardFile: String): ImageIcon = {
    val myIcon2 = new ImageIcon("images/cardsH/" + cardFile + ".png")
    val img = myIcon2.getImage();
    val newimg = img.getScaledInstance(54, 40, java.awt.Image.SCALE_SMOOTH)
    val myIcon1 = new ImageIcon(newimg)
    myIcon1
  }

  // Show a player has split his or her hand
  def showSplit(player: Int): Unit = {
    // Move the second card from the hand to the first card of the same player's second hand
    // if the player is on the left side of the table, the second card dealt is on the right
    if (player < 3)
      p.panelForPlayer(player)(1).contents.append(p.panelForPlayer(player)(0).contents(1))
    // else if the player is on the right side of the table, the second card dealt is on the left
    else
      p.panelForPlayer(player)(1).contents.append(p.panelForPlayer(player)(0).contents(0))
    // Display the split bar to indicate that there are two hands on the table
    p.playerDivider(player).icon = new ImageIcon("images/SplitBar.png")
  }

  // Prompt the player to place his or her bet
  def promptToBet(): Unit = {
    top.betButton.enabled = true
    actionLabel.text = "Place your bet!"
  }

  // Prompt the player to play his or her hand
  def promptToPlay(splitPossible: Boolean): Unit = {
    top.hitButton.enabled = true
    top.standButton.enabled = true
    top.doubleDownButton.enabled = true
    top.surrenderButton.enabled = true
    if (splitPossible)
      top.splitButton.enabled = true
    actionLabel.text = "Play your hand!"
  }

  // Inform the player that he or she broke
  def playerBroke(): Unit = {
    top.actbuttonPanel.contents.foreach(x => x.enabled = false)
    actionLabel.text = "You broke!"
    top.patterArea.text += "You broke!\n"
  }

  // React to the fact that the player chose to stay
  def playerStood(): Unit = {
    top.actbuttonPanel.contents.foreach(x => x.enabled = false)
    actionLabel.text = "You chose to stay!"
    top.patterArea.text += "You chose to stay\n"
  }

  // React to the fact that the player doubled down
  def playerDoubled(): Unit = {
    top.actbuttonPanel.contents.foreach(x => x.enabled = false)
    actionLabel.text = "You doubled down!"
    top.patterArea.text += "You doubled down\n"
  }

  // React to the fact that the player surrendered
  def playerSurrendered(): Unit = {
    top.actbuttonPanel.contents.foreach(x => x.enabled = false)
    actionLabel.text = "You surrendered!"
    top.patterArea.text += "You surrendered\n"
  }

  // React to the fact that the player hit
  def playerHit(): Unit = {
    actionLabel.text = "You hit"
    top.patterArea.text += "You hit!\n"
    top.splitButton.enabled = false
    top.doubleDownButton.enabled = false
    top.surrenderButton.enabled = false
  }

  // An updated array of table data has been received from the HumanPlayer.  
  // Update the players and their cards accordingly.  
  def setTable(status: Array[(String, List[Card])]): Unit = {
    (0 to 5).foreach(x => set(status(x), x))
    def set(stat: (String, List[Card]), playerNum: Int): Unit = {
      if (stat != null) {
        p.nameLabels(playerNum).text = stat._1
        //          stat._2.foreach(x => showCard(x, playerNum,false))  //AMC: Figure out how to re-enable this
        /*          if(stat._2.size<2) {
            ((stat._2.size) to 1).foreach(x => showCard("back",playerNum))
          }*/
      } else {
        p.nameLabels(playerNum).text = "_________"
      }
    }
  }

  //clear all cards from the table; the game is over
  def clearTable(): Unit = {
    // Clear all cards from both hands for each player
    (0 to 5).foreach(player => (0 to 1).foreach(hand => p.panelForPlayer(player)(hand).contents.remove(0, p.panelForPlayer(player)(hand).contents.length)))
    // Clear split dividing bar for each player (will likely already be null)
    (0 to 5).foreach(player => p.playerDivider(player).icon = null)
    // Clear all the dealer's cards
    p.dealerCards.contents.remove(0, p.dealerCards.contents.length)
    // Set the upcard and downcard to a blank box
    p.dealerUp.icon = cardIcon("blank")
    p.dealerDown.icon = cardIcon("blank")
    // Add the upcard and downcard (blank) to the dealer's display
    p.dealerCards.contents += p.dealerUp
    p.dealerCards.contents += p.dealerDown
  }

  // Leave the table. Do this by pretending the Leave Table button was clicked. See how a click of this button is handled by the GUI
  def standUpFromTable(): Unit = {
    top.leaveButton.doClick()
  }

}


